import paho.mqtt.client as mqtt
import json
import logging
from datetime import datetime
from config import config
from models import Device, HeartbeatLog, DeviceLog
from apscheduler.schedulers.background import BackgroundScheduler

logger = logging.getLogger(__name__)


class MQTTClient:
    """MQTT客户端管理类"""
    
    def __init__(self):
        self.client = None
        self.connected = False
        self.scheduler = BackgroundScheduler()
        self.scheduler.start()
        
    def on_connect(self, client, userdata, flags, rc):
        """连接回调"""
        if rc == 0:
            self.connected = True
            logger.info("成功连接到MQTT Broker")
            
            # 订阅所有设备的心跳主题
            topic = "/iot/+/heartbeat"
            client.subscribe(topic, qos=1)
            logger.info(f"订阅主题: {topic}")
            
            # 启动心跳超时检查任务
            self._schedule_heartbeat_check()
        else:
            self.connected = False
            logger.error(f"MQTT连接失败，返回码: {rc}")
    
    def on_disconnect(self, client, userdata, rc):
        """断开连接回调"""
        self.connected = False
        if rc != 0:
            logger.warning(f"意外断开MQTT连接，返回码: {rc}")
        else:
            logger.info("MQTT连接已断开")
    
    def on_message(self, client, userdata, msg):
        """消息接收回调"""
        try:
            # 解析主题获取设备ID
            # 主题格式: /iot/{deviceId}/heartbeat
            topic_parts = msg.topic.split('/')
            if len(topic_parts) >= 3:
                device_id = topic_parts[2]
                
                # 解析消息内容
                payload = json.loads(msg.payload.decode('utf-8'))
                logger.debug(f"收到设备 {device_id} 的心跳: {payload}")
                
                # 处理心跳消息
                self._handle_heartbeat(device_id, payload)
            else:
                logger.warning(f"无效的主题格式: {msg.topic}")
        except json.JSONDecodeError as e:
            logger.error(f"JSON解析失败: {e}, 消息: {msg.payload}")
        except Exception as e:
            logger.error(f"处理消息时发生错误: {e}")
    
    def _handle_heartbeat(self, device_id: str, payload: dict):
        """处理心跳消息"""
        try:
            # 查找设备
            device = Device.find_by_id(device_id)
            
            if not device:
                # 设备不存在，自动注册
                logger.info(f"设备 {device_id} 不存在，自动注册")
                Device.create({
                    'deviceId': device_id,
                    'deviceName': f'设备_{device_id}'
                })
                device = Device.find_by_id(device_id)
            
            # 获取当前状态
            old_status = device.get('status', 'offline')
            new_status = payload.get('status', 'online')
            
            # 更新设备状态
            Device.update_status(device_id, new_status)
            
            # 记录心跳日志
            HeartbeatLog.create(device_id, payload)
            
            # 如果状态变化，记录设备日志
            if old_status != new_status:
                if new_status == 'online':
                    DeviceLog.create(device_id, 'online', f'设备上线')
                    logger.info(f"设备 {device_id} 上线")
                elif new_status == 'offline':
                    DeviceLog.create(device_id, 'offline', f'设备离线')
                    logger.info(f"设备 {device_id} 离线")
            
        except Exception as e:
            logger.error(f"处理心跳时发生错误: {e}")
    
    def _schedule_heartbeat_check(self):
        """启动心跳超时检查定时任务"""
        # 每30秒检查一次
        self.scheduler.add_job(
            self._check_heartbeat_timeout,
            'interval',
            seconds=30,
            id='heartbeat_check',
            replace_existing=True
        )
        logger.info("心跳超时检查任务已启动")
    
    def _check_heartbeat_timeout(self):
        """检查设备心跳超时"""
        try:
            from datetime import timedelta
            
            # 计算超时时间
            timeout_seconds = config.HEARTBEAT_TIMEOUT
            cutoff_time = datetime.utcnow() - timedelta(seconds=timeout_seconds)
            
            # 查找所有在线设备
            online_devices = Device.find_all({'status': 'online'})
            
            for device in online_devices:
                last_heartbeat = device.get('lastHeartbeatTime')
                
                # 如果心跳超时，标记为离线
                if last_heartbeat and last_heartbeat < cutoff_time:
                    device_id = device['deviceId']
                    Device.update_status(device_id, 'offline')
                    DeviceLog.create(device_id, 'offline', f'心跳超时，设备离线')
                    logger.warning(f"设备 {device_id} 心跳超时，已标记为离线")
        
        except Exception as e:
            logger.error(f"检查心跳超时时发生错误: {e}")
    
    def connect(self):
        """连接到MQTT Broker"""
        try:
            self.client = mqtt.Client(client_id="iot_platform_server")
            
            # 设置认证信息
            if config.MQTT_USERNAME and config.MQTT_PASSWORD:
                self.client.username_pw_set(config.MQTT_USERNAME, config.MQTT_PASSWORD)
            
            # 设置回调
            self.client.on_connect = self.on_connect
            self.client.on_disconnect = self.on_disconnect
            self.client.on_message = self.on_message
            
            # 连接到Broker
            self.client.connect(
                config.MQTT_BROKER_HOST,
                config.MQTT_BROKER_PORT,
                config.MQTT_KEEPALIVE
            )
            
            # 启动网络循环
            self.client.loop_start()
            
            logger.info(f"正在连接MQTT Broker: {config.MQTT_BROKER_HOST}:{config.MQTT_BROKER_PORT}")
            return True
            
        except Exception as e:
            logger.error(f"连接MQTT Broker失败: {e}")
            return False
    
    def disconnect(self):
        """断开MQTT连接"""
        if self.client:
            self.client.loop_stop()
            self.client.disconnect()
            logger.info("MQTT客户端已断开")
        
        if self.scheduler.running:
            self.scheduler.shutdown()
            logger.info("调度器已关闭")
    
    def publish(self, topic: str, payload: dict, qos: int = 1):
        """发布消息"""
        if not self.connected:
            logger.error("MQTT未连接，无法发布消息")
            return False
        
        try:
            payload_json = json.dumps(payload)
            result = self.client.publish(topic, payload_json, qos=qos)
            
            if result.rc == mqtt.MQTT_ERR_SUCCESS:
                logger.debug(f"消息发布成功: {topic}")
                return True
            else:
                logger.error(f"消息发布失败: {topic}, 错误码: {result.rc}")
                return False
        except Exception as e:
            logger.error(f"发布消息时发生错误: {e}")
            return False


# 全局MQTT客户端实例
mqtt_client = MQTTClient()

